package com.aizuda.easyManagerTool.service.docker.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.aizuda.easy.security.domain.Rep;
import com.aizuda.easy.security.domain.Req;
import com.aizuda.easyManagerTool.config.socket.SocketSend;
import com.aizuda.easyManagerTool.domain.dto.PageDTO;
import com.aizuda.easyManagerTool.domain.dto.dbc.db.ForwardInfo;
import com.aizuda.easyManagerTool.domain.dto.docker.*;
import com.aizuda.easyManagerTool.domain.dto.monitor.MonitorEditDTO;
import com.aizuda.easyManagerTool.domain.dto.socket.SocketMessageDTO;
import com.aizuda.easyManagerTool.domain.dto.terminal.SSHInfoDTO;
import com.aizuda.easyManagerTool.domain.entity.docker.DockerListEntity;
import com.aizuda.easyManagerTool.domain.entity.monitor.MonitorUrlEntity;
import com.aizuda.easyManagerTool.domain.vo.PageVO;
import com.aizuda.easyManagerTool.domain.vo.docker.*;
import com.aizuda.easyManagerTool.domain.vo.server.ServerCompleteVO;
import com.aizuda.easyManagerTool.domain.vo.setting.SettingUserVO;
import com.aizuda.easyManagerTool.domain.vo.socket.SocketMessageVO;
import com.aizuda.easyManagerTool.mapper.docker.DockerConfigMapper;
import com.aizuda.easyManagerTool.mapper.docker.DockerListMapper;
import com.aizuda.easyManagerTool.mapper.server.ServerMapper;
import com.aizuda.easyManagerTool.service.docker.DockerListServer;
import com.aizuda.easyManagerTool.service.monitor.MonitorDataService;
import com.aizuda.easyManagerTool.service.socket.SocketEventService;
import com.aizuda.easyManagerTool.service.socket.impl.SocketSessionManager;
import com.aizuda.easyManagerTool.service.terminal.ScriptService;
import com.aizuda.easyManagerTool.service.terminal.impl.TerminalSessionManager;
import com.aizuda.easyManagerTool.util.AssertUtil;
import com.aizuda.easyManagerTool.util.SSHManagerUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.async.ResultCallback;
import com.github.dockerjava.api.command.*;
import com.github.dockerjava.api.exception.ConflictException;
import com.github.dockerjava.api.model.*;
import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl;
import com.github.dockerjava.httpclient5.ApacheDockerHttpClient;
import com.github.dockerjava.transport.DockerHttpClient;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Service;
import org.springframework.web.socket.TextMessage;
import org.springframework.web.socket.WebSocketSession;

import javax.annotation.Resource;
import java.io.IOException;
import java.io.InputStream;
import java.io.PipedInputStream;
import java.io.PipedOutputStream;
import java.time.Duration;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.BiConsumer;
import java.util.stream.Collectors;

@Slf4j
@Service(value = "docker")
public class DockerListServerImpl extends ServiceImpl<DockerListMapper, DockerListEntity> implements DockerListServer, CommandLineRunner, SocketEventService {

    @Resource
    private DockerListMapper dockerListMapper;
    @Resource
    private DockerConfigMapper dockerConfigMapper;
    @Resource
    private ScriptService scriptService;
    @Resource
    private ServerMapper serverMapper;
    @Value("${server.port}")
    private String port;
    @Value("${config.intranet-ip}")
    private String intranetIp;
    @Resource
    MonitorDataService monitorDataService;

    @Override
    public Rep<PageVO<DockerListVO>> find(Req<PageDTO<DockerListEntity>, SettingUserVO> req) {
        PageDTO<DockerListEntity> pageDTO = req.getData();
        DockerListEntity data = pageDTO.getData();
        Page<DockerListEntity> page = new Page<DockerListEntity>(pageDTO.getCurrent(), pageDTO.getSize());
        // 查询 唯一条件是要根据 权限查询
        IPage<DockerListVO> dockerListVOIPage = dockerListMapper.find(page, ObjectUtil.isEmpty(data)? new DockerListEntity():data);
        // 从缓存查询
        dockerListVOIPage.getRecords().parallelStream().forEach(item ->{
            DockerManagerVO dockerManagerVO = DockerManager.get(item.getId());
            if(ObjectUtil.isEmpty(dockerManagerVO) || !dockerManagerVO.isState()){
                return;
            }
            item.setState(dockerManagerVO.isState());
            item.setServerCpu(dockerManagerVO.getInfo().getNCPU());
            item.setServerMemory(dockerManagerVO.getInfo().getMemTotal());
            item.setDockerRootDir(dockerManagerVO.getInfo().getDockerRootDir());
            item.setImageNum(dockerManagerVO.getImages().size());
            item.setContainerNum(dockerManagerVO.getInfo().getContainers());
            item.setNetworkNum(dockerManagerVO.getNetworks().size());
            item.setVolumeNum(dockerManagerVO.getVolumes().getVolumes().size());
        });
        List<DockerListVO> sort = CollUtil.sort(dockerListVOIPage.getRecords(), Comparator.comparing(DockerListVO::getState).reversed());
        PageVO<DockerListVO> pageVO = new PageVO<DockerListVO>(pageDTO)
                .setTotal(dockerListVOIPage.getTotal())
                .setRecords(sort);
        return Rep.ok(pageVO);
    }

    @Override
    public Rep<DockerListVO> edit(Req<DockerListEntity, SettingUserVO> req) {
        DockerListEntity data = req.getData();
        if(CollUtil.isEmpty(data.getServerIds())){
            return Rep.error(500,"请选择服务器");
        }
        if(!data.getListSsh()){
            AssertUtil.objIsNull(data.getListPort(), "请填写tcp端口");
        }
        // 查询重复
        List<DockerListEntity> dockerListEntities = dockerListMapper.selectByServerIds(data.getServerIds());
        if(CollUtil.isNotEmpty(dockerListEntities)){
            return Rep.error(500,"请勿重复添加");
        }
        List<DockerListEntity> collect = data.getServerIds().stream().map(i -> {
            DockerListEntity dockerListEntity = BeanUtil.copyProperties(data,DockerListEntity.class);
            dockerListEntity.setServerId(i);
            dockerListEntity.setTenantId(req.getData().getTenantId());
            dockerListEntity.setCreateTime(new Date());
            dockerListEntity.setUpdateTime(new Date());
            return dockerListEntity;
        }).collect(Collectors.toList());
        // 通知更新
        saveOrUpdateBatch(collect);
        init();
        return Rep.ok();
    }

    @Override
    public Rep<DockerListVO> del(Req<DockerListEntity, SettingUserVO> req) {
        DockerListEntity data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        dockerListMapper.deleteById(data.getId());
        DockerManager.del(data.getId());
        return Rep.ok();
    }

    @Override
    public void refresh() {
        DockerManager.getList().parallelStream().forEach(i -> {
            i.refresh();
        });
    }

    @Override
    public Rep<List<Image>> imageList(Req<DockerListImageDTO, SettingUserVO> req) {
        DockerListImageDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());
        dockerManagerVO.refresh();
        return Rep.ok(dockerManagerVO.getImages());
    }

    @Override
    public Rep<List<Image>> imageDel(Req<DockerListImageDTO, SettingUserVO> req) {
        DockerListImageDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        AssertUtil.objIsNull(data.getImageId(),"数据错误");
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());
        try {
            RemoveImageCmd removeImageCmd = dockerManagerVO.getDockerClient().removeImageCmd(data.getImageId());
            removeImageCmd.withForce(true);
            removeImageCmd.exec();
        }catch (ConflictException e){
            if (e.getMessage().startsWith("Status 409")) {
                return Rep.error(500,"请先删除该镜像所运行的容器");
            }
        }
        return this.imageList(req);
    }

    @Override
    public Rep<List<Image>> imageTagDel(Req<DockerListImageDTO, SettingUserVO> req) {
        DockerListImageDTO data = req.getData();
        SettingUserVO user = req.getUser();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        AssertUtil.objIsNull(data.getImageId(),"数据错误");
        AssertUtil.objIsNull(data.getImageName(),"数据错误");
        DockerListEntity dockerListEntity = dockerListMapper.selectById(data.getId());
        ServerCompleteVO byId = serverMapper.findById(dockerListEntity.getServerId());
        SSHInfoDTO sshInfoDTO = new SSHInfoDTO();
        BeanUtil.copyProperties(byId,sshInfoDTO);
        String sb = scriptService.execScript(null,user,sshInfoDTO,"docker rmi -f "+data.getImageName());
        AssertUtil.objIsNull(sb, "删除失败");
        return this.imageList(req);
    }

    @Override
    public Rep<List<Image>> imagePush(Req<DockerListImageDTO, SettingUserVO> req) {
        DockerListImageDTO data = req.getData();
        SettingUserVO user = req.getUser();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        AssertUtil.objIsNull(data.getImageId(),"数据错误");
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());

        exec(data,dockerManagerVO,req.getUser(),(webSocketSession, authConfig) -> {
            // 制作新的tag
            dockerManagerVO.getDockerClient()
                    .tagImageCmd(data.getImageId(), data.getNewImageName(), data.getNewTagName())
                    .exec();
            // 推送
            dockerManagerVO.getDockerClient()
                    .pushImageCmd(data.getNewImageName()+":"+data.getNewTagName())
                    .withAuthConfig(authConfig)
                    .exec(new ExecCalElBack(
                            SocketSessionManager.get(req.getUser()),
                            "dockerListImage"+data.getImageId()
                    ));
        });

        return this.imageList(req);
    }

    @Override
    public Rep<List<Image>> imagePull(Req<DockerListImageDTO, SettingUserVO> req) {
        DockerListImageDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        AssertUtil.objIsNull(data.getNewImageName(),"请填写镜像名称");
        AssertUtil.objIsNull(data.getNewTagName(),"请填写Tag");
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());
        exec(data,dockerManagerVO,req.getUser(),(webSocketSession, authConfig) -> {
            // 拉取
            dockerManagerVO.getDockerClient().pullImageCmd(data.getNewImageName())
                    .withAuthConfig(authConfig)
                    .withTag(data.getNewTagName())
                    .exec(new ExecCalElBack(
                            SocketSessionManager.get(req.getUser()),
                            "pullImag"+data.getNewImageName()+":"+data.getNewTagName()
                    ));
        });
        return Rep.ok(dockerManagerVO.getImages());
    }

    @Override
    public Rep<String> imageInfo(Req<DockerListImageDTO, SettingUserVO> req) {
        DockerListImageDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        AssertUtil.objIsNull(data.getImageId(),"数据错误");
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());
        InspectImageResponse exec = dockerManagerVO.getDockerClient().inspectImageCmd(data.getImageId()).exec();
        return Rep.ok(JSONUtil.toJsonStr(exec));
    }


    private void exec(DockerListImageDTO data, DockerManagerVO dockerManagerVO, SettingUserVO userVO, BiConsumer<WebSocketSession,AuthConfig> consumer){
        WebSocketSession webSocketSession = SocketSessionManager.get(userVO);
        AuthConfig authConfig = dockerManagerVO.getDockerClient().authConfig();
        if(ObjectUtil.isNotEmpty(data.getConfigId())) {
            DockerConfigFindVO dockerConfigFindVO = dockerConfigMapper.findById(data.getConfigId());
            // 替换为你的私有仓库地址
            authConfig
                    .withRegistryAddress(dockerConfigFindVO.getConfigUrl())
                    .withUsername(dockerConfigFindVO.getConfigUserName())
                    .withPassword(dockerConfigFindVO.getConfigPassword());
        }else{
            authConfig = null;
        }
        consumer.accept(webSocketSession,authConfig);
    }


    @Override
    public Rep<List<Container>> containerList(Req<DockerListContainerDTO, SettingUserVO> req) {
        DockerListContainerDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());
        return Rep.ok(dockerManagerVO.getContainers().stream()
                .sorted(Comparator.comparing(Container::getState).reversed())
                .collect(Collectors.toList()));
    }

    @Override
    public Rep<List<Container>> containerRun(Req<DockerListContainerDTO, SettingUserVO> req) {
        DockerListContainerDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        if(CollUtil.isEmpty(data.getContainerId())){
            return Rep.error(500,"请选择容器");
        }
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());
        data.getContainerId().parallelStream().forEach(i -> {
            dockerManagerVO.getDockerClient().restartContainerCmd(i).exec();
        });
        dockerManagerVO.refresh();
        return this.containerList(req);
    }

    @Override
    public Rep<List<Container>> containerStop(Req<DockerListContainerDTO, SettingUserVO> req) {
        DockerListContainerDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        if(CollUtil.isEmpty(data.getContainerId())){
            return Rep.error(500,"请选择容器");
        }
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());
        data.getContainerId().parallelStream().forEach(i -> {
            dockerManagerVO.getDockerClient().stopContainerCmd(i).exec();
        });
        dockerManagerVO.refresh();
        return this.containerList(req);
    }

    @Override
    public Rep<List<Container>> containerDel(Req<DockerListContainerDTO, SettingUserVO> req) {
        DockerListContainerDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        if(CollUtil.isEmpty(data.getContainerId())){
            return Rep.error(500,"请选择容器");
        }
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());
        data.getContainerId().parallelStream().forEach(i -> {
            dockerManagerVO.getDockerClient().removeContainerCmd(i).exec();
        });
        dockerManagerVO.refresh();
        return this.containerList(req);
    }

    @Override
    public Rep<List<Container>> containerLog(Req<DockerListContainerDTO, SettingUserVO> req) {
        DockerListContainerDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        if(CollUtil.isEmpty(data.getContainerId())){
            return Rep.error(500,"请选择容器");
        }
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());

        dockerManagerVO.getDockerClient().logContainerCmd(data.getContainerId().get(0))
                // 是否包含标准输出
                .withStdOut(true)
                // 是否包含标准错误输出
                .withStdErr(true)
                // 从当前时间往前获取日志
                .withSince(0)
                .withTail(data.getNum())
                // 是否跟随日志输出
                .withFollowStream(false)
                .exec(new TerminalCalElBack(
                        SocketSessionManager.get(req.getUser()),
                        "containerLog"+data.getContainerId().get(0)
                ));
        return Rep.ok();
    }

    Map<String,PipedOutputStream> map = new ConcurrentHashMap<>();

    @Override
    public Rep<List<Container>> containerTerminal(Req<DockerListContainerDTO, SettingUserVO> req) {
        DockerListContainerDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        if(CollUtil.isEmpty(data.getContainerId())){
            return Rep.error(500,"请选择容器");
        }
        DockerListVO item = dockerListMapper.findById(data.getId());
        ServerCompleteVO byId = serverMapper.findById(item.getServerId());
        DockerManagerVO dockerManagerVO = new DockerManagerVO();
        try {
            DockerClientConfig config = createDockerClientConfig(item,byId,dockerManagerVO);
            DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
                    .dockerHost(config.getDockerHost())
                    .maxConnections(1)
                    .connectionTimeout(Duration.ofSeconds(10))
                    // 2分钟不动 自动超时
                    .responseTimeout(Duration.ofSeconds(120))
                    .build();
            DockerClient dockerClient = DockerClientImpl.getInstance(config, httpClient);
            ExecCreateCmdResponse sh = dockerClient
                    .execCreateCmd(data.getContainerId().get(0))
                    .withCmd(data.getShMode())
                    .withTty(true)
                    .withAttachStdin(true)
                    .withAttachStdout(true)
                    .withAttachStderr(false)
                    .withPrivileged(true)
                    .exec();
            PipedOutputStream outputStream = new PipedOutputStream();
            InputStream inputStream = new PipedInputStream(outputStream);
            map.put(data.getRandomId(),outputStream);
            dockerClient.execStartCmd(sh.getId())
                    .withStdIn(inputStream)
                    .exec(new TerminalCalElBack(
                    SocketSessionManager.get(req.getUser()),
                    "containerTerminal"+data.getRandomId()
            ){
                @Override
                public void onError(Throwable throwable) {
                    try {
                        SocketMessageVO<String> socketMessageVO = BeanUtil.copyProperties(super.socketMessage, SocketMessageVO.class);
                        socketMessageVO.setType(super.type);
                        if (throwable.getMessage().contains("Read time out")) {
                            socketMessageVO.setObj("会话超时结束");
                            super.session.sendMessage(new TextMessage(JSONUtil.toJsonStr(socketMessageVO)));
                        } else {
                            socketMessageVO.setObj(throwable.getMessage());
                            super.session.sendMessage(new TextMessage(JSONUtil.toJsonStr(socketMessageVO)));
                        }
                    }catch (Exception e){}
                }

                @SneakyThrows
                @Override
                public void onNext(Object object) {
                    Frame frame = (Frame) object;
                    SocketMessageVO<String> socketMessageVO = BeanUtil.copyProperties(super.socketMessage, SocketMessageVO.class);
                    String s = new String(frame.getPayload());
                    socketMessageVO.setType(super.type);
                    socketMessageVO.setObj(s);
                    super.session.sendMessage(new TextMessage(JSONUtil.toJsonStr(socketMessageVO)));
                }

                @SneakyThrows
                @Override
                public void onComplete() {
                    dockerClient.close();
                    map.remove(data.getRandomId());
                    dockerManagerVO.getManager().close();
                    log.debug("结束docker终端 "+data.getContainerId().get(0));
                    super.onComplete();
                }
            });
        }catch (Exception e){
            e.printStackTrace();
            return Rep.error(500,"连接失败："+e.getMessage());
        }
        return Rep.ok();
    }

    @Override
    public Rep<List<Container>> containerSynchronizationData(Req<DockerListContainerDTO, SettingUserVO> req) {
        DockerListContainerDTO data = req.getData();
        SettingUserVO user = req.getUser();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        AssertUtil.objIsNull(data.getContainerId(),"数据错误");
        // 得到容器信息
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());
        InspectContainerResponse exec = dockerManagerVO.getDockerClient().inspectContainerCmd(data.getContainerId().get(0)).exec();
        // 添加到数据源
        MonitorEditDTO monitorDataEntity = new MonitorEditDTO();
        monitorDataEntity.setTenantId(user.getTenantId());
        monitorDataEntity.setMdTitle(data.getName()+"-"+exec.getName());
        MonitorUrlEntity monitorUrlEntity = new MonitorUrlEntity();
        monitorUrlEntity.setUurl(intranetIp+":"+port+"/docker/monitor/"+data.getId()+"/"+exec.getId());
        monitorUrlEntity.setUtype("json");
        monitorDataEntity.setUrls(Arrays.asList(monitorUrlEntity));
        monitorDataService.edit(monitorDataEntity);
        return Rep.ok();
    }

    @Override
    public Rep<List<Container>> containerAdd(Req<DockerContainerAddDTO, SettingUserVO> req) {
        DockerContainerAddDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());
        AuthConfig authConfig = dockerManagerVO.getDockerClient().authConfig();
        if(ObjectUtil.isNotEmpty(data.getConfigId())) {
            DockerConfigFindVO dockerConfigFindVO = dockerConfigMapper.findById(data.getConfigId());
            // 替换为你的私有仓库地址
            authConfig
                    .withRegistryAddress(dockerConfigFindVO.getConfigUrl())
                    .withUsername(dockerConfigFindVO.getConfigUserName())
                    .withPassword(dockerConfigFindVO.getConfigPassword());
        }else{
            authConfig = null;
        }
        HostConfig hostConfig = HostConfig.newHostConfig();
        // 基础配置
        CreateContainerCmd createContainerCmd = dockerManagerVO.getDockerClient().createContainerCmd(data.getImage())
                .withName(data.getName())
                .withCmd(data.getCommand())
                .withRestartPolicy(RestartPolicy.parse(data.getStrategy()));
        if(ObjectUtil.isNotEmpty(authConfig)){
            createContainerCmd = createContainerCmd.withAuthConfig(authConfig);
        }
        // 端口配置
        if(CollUtil.isNotEmpty(data.getPorts())){
            List<PortBinding> portBindings = data.getPorts().stream().map(i -> PortBinding.parse(i.f0 + ":" + i.f1)).collect(Collectors.toList());
            List<ExposedPort> exposedPorts = data.getPorts().stream().map(i -> new ExposedPort(i.f2, i.f1)).collect(Collectors.toList());
            hostConfig = hostConfig.withPortBindings(portBindings);
            createContainerCmd = createContainerCmd.withExposedPorts(exposedPorts);
        }
        // 卷
        if(CollUtil.isNotEmpty(data.getVolumes())){
            List<Bind> binds = data.getVolumes().stream().map(i -> new Bind(i.f0, new Volume(i.f1))).collect(Collectors.toList());
            hostConfig = hostConfig.withBinds(binds);
        }
        // 标签
        if(CollUtil.isNotEmpty(data.getLabes())){
            Map<String,String> labels = new HashMap<>();
            data.getLabes().forEach(i -> labels.put(i.f0, i.f1));
            createContainerCmd = createContainerCmd.withLabels(labels);
        }
        // 网络
        if(StrUtil.isNotEmpty(data.getNetwork().getModel())){
            createContainerCmd = createContainerCmd.withNetworkMode(data.getNetwork().getModel());
        }
        if(StrUtil.isNotEmpty(data.getNetwork().getHostName())){
            createContainerCmd = createContainerCmd.withHostName(data.getNetwork().getHostName());
        }
        if(StrUtil.isNotEmpty(data.getNetwork().getDomainName())){
            createContainerCmd = createContainerCmd.withDomainName(data.getNetwork().getDomainName());
        }
        if(StrUtil.isNotEmpty(data.getNetwork().getIpv4Address())){
            createContainerCmd = createContainerCmd.withIpv4Address(data.getNetwork().getIpv4Address());
        }
        if(StrUtil.isNotEmpty(data.getNetwork().getIpv6Address())){
            createContainerCmd = createContainerCmd.withIpv6Address(data.getNetwork().getIpv6Address());
        }
        if(StrUtil.isNotEmpty(data.getNetwork().getMacAddress())){
            createContainerCmd = createContainerCmd.withMacAddress(data.getNetwork().getMacAddress());
        }
        if(StrUtil.isNotEmpty(data.getNetwork().getPrimaryDNSServer())){
            createContainerCmd = createContainerCmd.withDns(data.getNetwork().getPrimaryDNSServer(),data.getNetwork().getSecondaryDNSServer());
        }
        // 资源
        if(ObjectUtil.isNotEmpty(data.getMemoryReserved())){
            // 确保容器不会长时间占用超过--memory-reservation限制的内存大小
            hostConfig = hostConfig.withMemoryReservation((long) (data.getMemoryReserved() * Math.pow(1024, 2)));
        }
        if(ObjectUtil.isNotEmpty(data.getMemoryLimit())){
            hostConfig = hostConfig.withMemory((long) (data.getMemoryLimit() * Math.pow(1024, 2)));
        }
        if(ObjectUtil.isNotEmpty(data.getCpuNum())){
            hostConfig = hostConfig.withCpuCount(data.getCpuNum());
        }
        // 执行
        createContainerCmd.withHostConfig(hostConfig).exec();
        dockerManagerVO.refresh();
        return Rep.ok(dockerManagerVO.getContainers().stream()
                .sorted(Comparator.comparing(Container::getState).reversed())
                .collect(Collectors.toList()));
    }

    @Override
    public Rep<String> containerInfo(Req<DockerListContainerDTO, SettingUserVO> req) {
        DockerListContainerDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        AssertUtil.objIsNull(data.getContainerId(),"数据错误");
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());
        InspectContainerResponse exec = dockerManagerVO.getDockerClient().inspectContainerCmd(data.getContainerId().get(0)).exec();
        return Rep.ok(JSONUtil.toJsonStr(exec));
    }

    @Override
    public Rep<List<DockerVolumeListDTO>> volumeList(Req<DockervolumeDTO, SettingUserVO> req) {
        DockervolumeDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());
        dockerManagerVO.refresh();
        List<Container> containers = dockerManagerVO.getContainers();
        List<InspectVolumeResponse> volumes = dockerManagerVO.getVolumes().getVolumes();
        List<DockerVolumeListDTO> use = volumes.parallelStream().map(item -> {
            DockerVolumeListDTO dockerVolumeListDTO = BeanUtil.copyProperties(item, DockerVolumeListDTO.class);
            dockerVolumeListDTO.setState("unUse");
            containers.forEach(i -> {
                i.getMounts().forEach(k -> {
                    if (item.getName().equals(k.getName())) {
                        dockerVolumeListDTO.setContainerName(String.join(",",i.getNames()));
                        dockerVolumeListDTO.setState("use");
                    }
                });
            });
            return dockerVolumeListDTO;
        }).sorted(Comparator.comparing(DockerVolumeListDTO::getState)
                .thenComparing(DockerVolumeListDTO::getName).reversed())
                .collect(Collectors.toList());
        return Rep.ok(use);
    }

    @Override
    public Rep<List<DockerVolumeListDTO>> volumeAdd(Req<DockervolumeDTO, SettingUserVO> req) {
        DockervolumeDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        Map<String,String> map = new HashMap<>();
        if(CollUtil.isNotEmpty(data.getDriverOpts())){
            data.getDriverOpts().forEach(i -> map.putAll(i));
        }
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());
        CreateVolumeCmd createVolumeCmd = dockerManagerVO.getDockerClient()
                .createVolumeCmd()
                .withName(data.getName())
                .withDriver(data.getDriver());
//                .withLabels(data.getLabels())
        if(map.keySet().size() > 0) {
            createVolumeCmd.withDriverOpts(map);
        }
        createVolumeCmd.exec();
        return this.volumeList(req);
    }

    @Override
    public Rep<List<DockerVolumeListDTO>> volumeDel(Req<DockervolumeDTO, SettingUserVO> req) {
        DockervolumeDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        AssertUtil.objIsNull(data.getName(),"数据错误");
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());
        dockerManagerVO.getDockerClient().removeVolumeCmd(data.getName()).exec();
        return this.volumeList(req);
    }

    @Override
    public Rep<List<DockerNetworkListVO>> networkList(Req<DockerNetworkDTO, SettingUserVO> req) {
        DockerNetworkDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());
        dockerManagerVO.refresh();
        List<Network> networks = dockerManagerVO.getNetworks();
        List<DockerNetworkListVO> collect = networks.parallelStream().map(i -> {
            DockerNetworkListVO dockerNetworkListVO = BeanUtil.copyProperties(i, DockerNetworkListVO.class);
            dockerNetworkListVO.setState("unUse");
            return dockerNetworkListVO;
        }).sorted(Comparator.comparing(DockerNetworkListVO::getState)
                        .thenComparing(DockerNetworkListVO::getId).reversed())
                .collect(Collectors.toList());
        return Rep.ok(collect);
    }

    @Override
    public Rep<List<DockerNetworkListVO>> networkAdd(Req<DockerNetworkDTO, SettingUserVO> req) {
        DockerNetworkDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());
        dockerManagerVO.getDockerClient().createNetworkCmd()
                .withName(data.getName())
                .withDriver(data.getDriver())
                .withOptions(data.getOptions())
                .withInternal(data.getInternal())
                .withAttachable(data.getAttachable())
                .withEnableIpv6(data.getEnableIpv6())
//                .withIpam(data.getIpam())
                .withLabels(data.getLabels())
                .withCheckDuplicate(data.getCheckDuplicate())
                .exec();
        return this.networkList(req);
    }

    @Override
    public Rep<List<DockerNetworkListVO>> networkDel(Req<DockerNetworkDTO, SettingUserVO> req) {
        DockerNetworkDTO data = req.getData();
        AssertUtil.objIsNull(data.getId(),"数据错误");
        AssertUtil.objIsNull(data.getNetworkId(),"数据错误");
        DockerManagerVO dockerManagerVO = DockerManager.get(data.getId());
        dockerManagerVO.getDockerClient().removeNetworkCmd(data.getNetworkId())
                .exec();
        return this.networkList(req);
    }



    @Override
    public void onMessage(String token, WebSocketSession session, SocketMessageDTO message) throws IOException {
        JSONObject jsonObject = (JSONObject) message.getJson();
        SSHDockerDTO sshDockerDTO = jsonObject.toBean(new TypeReference<SSHDockerDTO>() {});
        // 把内容写到流里面
        PipedOutputStream pipedOutputStream = map.get(sshDockerDTO.getRandomId());
        if(ObjectUtil.isEmpty(pipedOutputStream)){return;}
        pipedOutputStream.write(sshDockerDTO.getCmd().getBytes());
        pipedOutputStream.flush();
    }

    public void init(){
        Thread thread = new Thread(() -> {
            List<DockerListVO> dockerListEntityList = dockerListMapper.findAll();
            List<ServerCompleteVO> byId = serverMapper.findByIdsNoTenantId(dockerListEntityList.stream().map(DockerListVO::getServerId).collect(Collectors.toList()));
            dockerListEntityList.parallelStream().forEach(item -> {
                try {
                    DockerManagerVO dockerManagerVO = new DockerManagerVO();
                    Optional<ServerCompleteVO> first = byId.stream().filter(i -> i.getId().equals(item.getServerId())).findFirst();
                    DockerClientConfig config = createDockerClientConfig(item,first.orElse(null),dockerManagerVO);
                    DockerHttpClient httpClient = new ApacheDockerHttpClient.Builder()
                            .dockerHost(config.getDockerHost())
                            .maxConnections(15)
                            .connectionTimeout(Duration.ofSeconds(5))
                            .responseTimeout(Duration.ofSeconds(30))
                            .build();
                    DockerClient dockerClient = DockerClientImpl.getInstance(config, httpClient);
                    dockerManagerVO.setDockerClient(dockerClient);
                    DockerManager.put(item.getId(), dockerManagerVO);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        });
        thread.setName("Docker服务初始化");
        thread.start();
    }

    private DockerClientConfig createDockerClientConfig(DockerListVO item,ServerCompleteVO serverCompleteVO,DockerManagerVO dockerManagerVO) throws Exception {
        String url = "tcp://" + item.getServerIp() + ":" + item.getListPort();
        if(item.getListSsh() && ObjectUtil.isNotEmpty(serverCompleteVO)){
            TerminalSessionManager.Manager manager = new TerminalSessionManager.Manager();
            ForwardInfo forwardInfo = new ForwardInfo();
            forwardInfo.setRPort(item.getListPort());
            forwardInfo.setRHost(item.getServerIp());
            forwardInfo.setLocalPort(item.getListLocalPort());
            SSHInfoDTO sshInfoDTO = BeanUtil.copyProperties(serverCompleteVO, SSHInfoDTO.class);
            sshInfoDTO.setForwardInfo(forwardInfo);
            SSHManagerUtil.forwardSession(manager,sshInfoDTO);
            url = "tcp://127.0.0.1:" + forwardInfo.getLocalPort();
            if(ObjectUtil.isNotEmpty(dockerManagerVO)) {
                dockerManagerVO.setManager(manager);
            }
        }
        return DefaultDockerClientConfig.createDefaultConfigBuilder()
                .withDockerHost(url)
                .withDockerTlsVerify(false)
                .build();
    }

    @Override
    public void run(String... args) throws Exception {
        init();
    }

    class ExecCalElBack<T> extends  ResultCallback.Adapter<T> {
        private SocketMessageVO<String> socketMessage = new SocketMessageVO<>();
        private WebSocketSession session;
        private String type;

        public ExecCalElBack(WebSocketSession webSocketSession, String type) {
            this.type = type;
            this.session = webSocketSession;
        }

        @Override
        public void onError(Throwable throwable) {
            SocketMessageVO<String> socketMessageVO = BeanUtil.copyProperties(socketMessage, SocketMessageVO.class);
            socketMessageVO.setObj(throwable.getMessage());
            socketMessageVO.setType(type);
            SocketSend.send(session,JSONUtil.toJsonStr(socketMessageVO));
            socketMessageVO.setObj("Execution error,status 500");
            SocketSend.send(session,JSONUtil.toJsonStr(socketMessageVO));
        }

        @Override
        public void onComplete() {
            SocketMessageVO<String> socketMessageVO = BeanUtil.copyProperties(socketMessage, SocketMessageVO.class);
            socketMessageVO.setObj("Execution successful, status 200");
            socketMessageVO.setType(type);
            SocketSend.send(session,JSONUtil.toJsonStr(socketMessageVO));
            super.onComplete();
        }

        @Override
        public void onNext(Object object) {
            ResponseItem responseItem = (ResponseItem) object;
            StringBuffer sb = new StringBuffer();
            sb.append(responseItem.getStatus());
            if (StrUtil.isNotEmpty(responseItem.getProgress())) {
                sb.append(responseItem.getProgress());
            }
            if (StrUtil.isNotEmpty(responseItem.getId())) {
                sb.append(responseItem.getId());
            }
            SocketMessageVO<String> socketMessageVO = BeanUtil.copyProperties(socketMessage, SocketMessageVO.class);
            socketMessageVO.setObj(sb.toString());
            socketMessageVO.setType(type);
            SocketSend.send(session,JSONUtil.toJsonStr(socketMessageVO));
        }
    }

    class TerminalCalElBack<T> extends  ResultCallback.Adapter<T> {
        private SocketMessageVO<String> socketMessage = new SocketMessageVO<>();
        private WebSocketSession session;
        private String type;

        public TerminalCalElBack(WebSocketSession webSocketSession, String type) {
            this.type = type;
            this.session = webSocketSession;
        }
        @Override
        public void onError(Throwable throwable) {
            SocketMessageVO<String> socketMessageVO = BeanUtil.copyProperties(socketMessage, SocketMessageVO.class);
            socketMessageVO.setObj(throwable.getMessage());
            socketMessageVO.setType(type);
            SocketSend.send(session,JSONUtil.toJsonStr(socketMessageVO));
        }

        @Override
        public void onNext(Object object) {
            Frame frame = (Frame) object;
            SocketMessageVO<String> socketMessageVO = BeanUtil.copyProperties(socketMessage, SocketMessageVO.class);
            String s = new String(frame.getPayload());
            socketMessageVO.setObj(s.replaceAll("RAW: ", ""));
            socketMessageVO.setType(type);
            SocketSend.send(session,JSONUtil.toJsonStr(socketMessageVO));
        }
    }


}
